package com.zqc

import com.zqc.annotation.MethodCost
import javassist.ClassPool
import javassist.CtClass
import javassist.CtConstructor
import javassist.CtField
import javassist.CtMethod
import javassist.NotFoundException
import org.gradle.api.Project

public class MyInject {

  private static ClassPool pool = ClassPool.getDefault()
  private static String injectStr = "System.out.println(\"I Love HuaChao\" ); ";

  // 插入代码，甚至可以替换第三方jar里面出错的方法
  public static void injectDir(Project project, String path, String packageName) {
    pool.appendClassPath(path)
    File dir = new File(path)
    if (dir.isDirectory()) {
      dir.eachFileRecurse { File file ->

        String filePath = file.absolutePath

        project.logger.error "========== MyInject.injectDir filePath: $filePath ==========="

        //确保当前文件是class文件，并且不是系统自动生成的class文件
        if (filePath.endsWith(".class")
            && !filePath.contains('R$')
            && !filePath.contains('R.class')
            && !filePath.contains("BuildConfig.class")) {
          // 判断当前目录是否是在我们的应用包里面
          int index = filePath.indexOf(packageName);
          boolean isMyPackage = index != -1;
          if (isMyPackage) {
            int end = filePath.length() - 6 // .class = 6
            String className = filePath.substring(index, end)
                .replace('\\', '.').replace('/', '.')
            //开始修改class文件
            CtClass c = pool.getCtClass(className)

            if (c.isFrozen()) {
              c.defrost()
            }

            // 1 test
//            try {
//              // 同理采用注解的方式获取哪些有MethodCost注解，然后加入如下代码即可
//              CtMethod method = c.getMethod("function", "()J")
//              if (method != null) {
//                project.logger.error "========== MyInject.injectDir insert method==========="
//
//                CtField field = new CtField(CtClass.longType, "start", c)
//                field.name = "start"
//                c.addField(field)
//
//                method.insertBefore("start = System.currentTimeMillis();")
//                method.insertAfter("long end = System.currentTimeMillis();\n" +
//                    "    System.out.println( \"cost:\" + (end - start));")
//              }
//            } catch(NotFoundException e) {
//
//            }
            // 2 annotation:在方法上加MetchodCost注解即可打印相应的方法耗时
            CtMethod[] methods = c.getMethods()
            if (methods != null) {
              for (CtMethod method : methods) {
                Object[] annotations = method.getAnnotations()
                if (annotations != null) {
                  for (Object obj : annotations) {
                    if (obj instanceof MethodCost) {
                      CtField field = new CtField(CtClass.longType, "start", c)
                      field.name = "start"
                      c.addField(field)

                      method.insertBefore("start = System.currentTimeMillis();")
                      method.insertAfter("long end = System.currentTimeMillis();\n" +
                          "    System.out.println( \"" + obj.value()
                          + " cost:\" + (end - start));start = 0l;")
                    }
                  }
                }
              }
            }

            CtConstructor[] cts = c.getDeclaredConstructors()
            if (cts == null || cts.length == 0) {
              //手动创建一个构造函数
              CtConstructor constructor = new CtConstructor(new CtClass[0], c)
              project.logger.error "========== MyInject.injectDir insert default==========="
              constructor.insertBeforeBody(injectStr)
              c.addConstructor(constructor)
            } else {
              project.logger.error "========== MyInject.injectDir insert first==========="

              cts[0].insertBeforeBody(injectStr)
            }
            c.writeFile(path)
            c.detach()
          }
        }
      }
    }
  }


}